package com.suray.system.run.seer.action;

import com.suray.basic.wcs.path.core.Node;
import com.suray.basic.wcs.plc.core.Plc;
import com.suray.basic.wcs.plc.pojo.DeviceBase;
import com.suray.basic.wcs.plc.pojo.oper.StationOper;
import com.suray.basic.wcs.utils.Coord;
import com.suray.basic.wcs.utils.LoggerUtil;
import com.suray.commin.core.utils.SpringContextHolder;
import com.suray.commin.core.utils.StringUtils;
import com.suray.system.run.seer.mysql.PointMapDB;
import com.suray.system.run.seer.util.PointManage;
import com.suray.wcs.service.core.util.LockNodeManager;
import com.suray.wcs.service.system.service.impl.NodeDBServiceImpl;
import com.suray.wcs.service.util.WcsUtil;

import java.util.Collections;
import java.util.List;
import java.util.Set;

/**
 * seer Agv 指定条件才能执行指定动作
 * seer Agv 指定位置执行特定的动作
 */
public class SeerAgvActions {

    private static NodeDBServiceImpl nodeDBService = SpringContextHolder.getBean(NodeDBServiceImpl.class);

    /**
     * 请求进入点位取货
     * @param point:AGV点位
     * @param object:请求对象
     * @return
     */
    public static boolean askGetCargoInPoint(String point, Object object) {
        //点位允许进入取货
        PointMapDB pointMapDB = PointManage.getPortMapByAgvNode(point);
        String mapNode = pointMapDB.getMapNode();
        if (StringUtils.isEmpty(mapNode)) {
            //没有地图点位, 不在系统中
            return true;
        }

        //在系统中的点位，有货，且点位未被锁
        return analyzePointAction(pointMapDB, object, true);
    }

    private static<T> boolean analyzePointAction(PointMapDB pointMapDB, T t, boolean hasCargo) {
        String mapNode = pointMapDB.getMapNode();

        switch (pointMapDB.getType()) {
            case PLC_TYPE:
                DeviceBase deviceBase = Plc.deviceBaseNameMap.get(pointMapDB.getPlcPoint());
                //工位有货且可取才允许进入
                StationOper stationOper = (StationOper) deviceBase;
                if (hasCargo) {
                    return stationOper.isCanGet() && stationOper.isPalletExist();
                } else {
                    return stationOper.isCanPut() && !stationOper.isPalletExist();
                }
            case LIFT_TYPE:
            case CONNECT_TYPE:
                //不是工位，需有货且未锁定
                Node node = Node.parseGrid(mapNode);
                if (WcsUtil.isNodeHasCargo(node, nodeDBService) == hasCargo) {
                    //锁定点位
                    if (lockNode(node, t)) {
                        pointMapDB.setLockObj(t.toString());
                        PointManage.updatePoint(pointMapDB);
                        return true;
                    }
                }
                break;
            case DEFAULT_TYPE:
                return true;
            case FORBIDDEN_TYPE:
            default:
                break;
        }
        return false;
    }

    /**
     * 请求进入点位放货
     * @param point:AGV点位
     * @param object:请求对象
     * @return
     */
    public static boolean askPutCargoInPoint(String point, Object object) {
        //点位允许进入取货
        PointMapDB pointMapDB = PointManage.getPortMapByAgvNode(point);
        String mapNode = pointMapDB.getMapNode();
        if (StringUtils.isEmpty(mapNode)) {
            //没有地图点位, 不在系统中
            return true;
        }

        //在系统中的点位，有货，且点位未被锁
        return analyzePointAction(pointMapDB, object, false);
    }

    /**
     * AGV离开点位
     * @param point:AGV点位
     * @param object:请求对象
     * @return
     */
    public static boolean askLeavePoint(String point, Object object) {
        PointMapDB pointMapDB = PointManage.getPortMapByAgvNode(point);
        String mapNode = pointMapDB.getMapNode();
        if (StringUtils.isEmpty(mapNode)) {
            //没有地图点位, 不在系统中
            LoggerUtil.info("无法获取对应的node，解锁点位失败:" + point, SeerAgvActions.class);
            return false;
        }
        if (unLockNode(Node.parseGrid(mapNode), object)) {
            pointMapDB.setLockObj("");
            PointManage.updatePoint(pointMapDB);
            return true;
        }
        return false;
    }

    /**
     * 请求更改货物状态
     * @param point:AGV点位
     * @param hasCargo:货物状态
     * @return
     */
    public static boolean askChangeCargoState(String point, boolean hasCargo) {
        PointMapDB pointMapDB = PointManage.getPortMapByAgvNode(point);
        String mapNode = pointMapDB.getMapNode();
        if (StringUtils.isEmpty(mapNode)) {
            LoggerUtil.info("无法获取对应的node，修改货物状态失败:" + point, SeerAgvActions.class);
            return false;
        }

        return true;
    }

    /**
     * 锁定点位
     * @param node
     * @param t
     * @return
     */
    private synchronized static<T> boolean lockNode(Node node, T t) {
        List<Node> nodeSet = Collections.singletonList(node);
        if (LockNodeManager.checkNodeListLockBy(nodeSet, t) == null) {
            LockNodeManager.lockedNodes(Collections.singleton(node), t);
            return true;
        }

        return false;
    }

    /**
     * 解锁点位
     * @param node
     * @param object
     * @return
     */
    private synchronized static boolean unLockNode(Node node, Object object) {
        Set<Coord> nodeSet = Collections.singleton(node);
        LockNodeManager.unlockedNodes(nodeSet, object);
        return true;
    }
}
